package uz.rt.dicom.data.element;

import uz.rt.dicom.ValueMultiplicity;
import uz.rt.dicom.ValueRepresentation;
import uz.rt.dicom.data.IDataElement;
import uz.rt.dicom.io.DicomIOException;
import uz.rt.dicom.io.DicomInputStream;
import uz.rt.dicom.tag.DicomTag;
import uz.rt.dicom.tag.Tag;

import java.io.IOException;
import java.util.ArrayList;

public class SequenceItem implements IDataElement
{
// ------------------------------ FIELDS ------------------------------

    public static final int LENGTH_UNDEFINED = 0xffffffff;
    public static final int LENGTH_NULL = 0x00000000;
    /** Item length */
    private int length;
    private DicomTag tag = null;
    private ValueMultiplicity vm = null;
    @SuppressWarnings({"FieldCanBeLocal"})
    private ValueRepresentation vr = null;
    private ArrayList<IDataElement> value = new ArrayList<IDataElement>();
    private String delimiter;

// --------------------------- CONSTRUCTORS ---------------------------

    public SequenceItem(DicomTag tag)
    {
        this.tag = tag;
        vm = null;
    }

// --------------------- GETTER / SETTER METHODS ---------------------

    public String getDelimiter()
    {
        return delimiter;
    }

    public void setDelimiter(String delimiter)
    {
        this.delimiter = delimiter;
    }

    public int getLength()
    {
        return length;
    }

    public DicomTag getTag()
    {
        return tag;
    }

    public void setTag(DicomTag tag)
    {
        this.tag = tag;
    }

    public Object getValue()
    {
        return value;
    }

    public ValueMultiplicity getVm()
    {
        return vm;
    }

// ------------------------ INTERFACE METHODS ------------------------


// --------------------- Interface IDataElement ---------------------

    public int getMaxLength()
    {
        return -1;
    }

    public byte[] getVR()
    {
        return ValueRepresentation.NA;
    }

    public ValueRepresentation getValueRepresentation()
    {
        return ValueRepresentation.NotApplicable;
    }

    public boolean isLengthFixed()
    {
        return (length != LENGTH_UNDEFINED);
    }

    public void setValueRepresentation(ValueRepresentation vr)
    {
        this.vr = vr;
    }

    public void read(DicomInputStream dicomInputStream, int length) throws IOException
    {
        this.length = length; // item length
        //TODO reread the reading process of sequence item values
        /**
         * if (!value.addAll(dicomInputStream.readSequenceItemValues(this.length)))
         throw new DicomIOException("No sequence item values were added");
         */

        DicomTag nextElementTag = dicomInputStream.readDicomTag();
        if (length == LENGTH_UNDEFINED)
        {
            while (nextElementTag != Tag.ItemDelimitationTag)
            {
                IDataElement element = dicomInputStream.readElement(tag);
                add(element);
                nextElementTag = dicomInputStream.readDicomTag();
            }
            long elementLength = dicomInputStream.readUnsigned32();
            if (elementLength > Integer.MAX_VALUE)
                throw new DicomIOException("Can not handle lengths that are bigger then " + Integer.MAX_VALUE);
            if (dicomInputStream.skip((int) elementLength) != elementLength)
                throw new DicomIOException(
                        "Number of bytes skipped in the dicom input stream differs from the expected number of bytes.");
        } else if (length != LENGTH_NULL)
        {
            int remaining = length;
            int pos;
            while (remaining > 0)
            {
                pos = dicomInputStream.getPosition();
                IDataElement element = dicomInputStream.readElement();
                remaining -= dicomInputStream.getPosition() - pos;
                add(element);
            }
            if (remaining < 0)
                throw new DicomIOException("Remaining sequence item length should equal zero at the end of " +
                                                   "a sequence, but found a remaining length that is lower then zero.");
        }

        /**
         this.length = length; // item length
         DicomTag nextElementTag = dicomInputStream.readDicomTag();
         long elementLength;
         if (length == LENGTH_UNDEFINED)
         {
         while (nextElementTag != Tag.ItemDelimitationTag)
         {
         elementLength = dicomInputStream.readUnsigned32();
         if (elementLength > Integer.MAX_VALUE)
         throw new DicomIOException("Can not handle lengths that are bigger then " + Integer.MAX_VALUE);
         IDataElement element = dicomInputStream.readElement();
         add(element);
         nextElementTag = dicomInputStream.readDicomTag();
         }
         elementLength = dicomInputStream.readUnsigned32();
         if (elementLength > Integer.MAX_VALUE)
         throw new DicomIOException("Can not handle lengths that are bigger then " + Integer.MAX_VALUE);
         if (dicomInputStream.skip((int) elementLength) != elementLength)
         throw new DicomIOException(
         "Number of bytes skipped in the dicom input stream differs from the expected number of bytes.");
         } else if (length != LENGTH_NULL)
         {
         int remaining = length;
         int pos;
         while(remaining > 0)
         {
         pos = dicomInputStream.getPosition();
         IDataElement element = dicomInputStream.readElement();
         remaining -= dicomInputStream.getPosition() - pos;
         add(element);
         }
         int remaining = length;
         int pos;
         while (remaining > 4)
         {
         elementLength = dicomInputStream.readUnsigned32();
         remaining -= 4; // remove 4 bytes after reading the element length
         if (elementLength > Integer.MAX_VALUE)
         throw new DicomIOException("Can not handle lengths that are bigger then " + Integer.MAX_VALUE);
         remaining -= (int) elementLength;
         if ((int) elementLength > remaining)
         throw new DicomIOException(
         "Element length exceeds the remains the remaining bytes in the sequence item.");
         pos = dicomInputStream.getPosition();
         IDataElement element = dicomInputStream.readElement();
         remaining -= dicomInputStream.getPosition() - pos;
         add(element);
         nextElementTag = dicomInputStream.readDicomTag();
         }
         if (remaining < 0)
         throw new DicomIOException("Remaining sequence item length should equal zero at the end of " +
         "a sequence, but found a remaining length that is lower then zero.");
         }
         */
    }

// -------------------------- OTHER METHODS --------------------------

    public void add(IDataElement element)
    {
        value.add(element);
    }

    public Object get(int i)
    {
        return value.get(i);
    }

    public void setValue(Object value)
    {
        //noinspection unchecked
        this.value = (ArrayList<IDataElement>) value;
    }
}
